package com.example.ioc.context;

import com.example.common.Statistical;
import com.example.common.utils.Statistics;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.*;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.core.ResolvableType;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;

import java.io.Serial;
import java.io.Serializable;

/**
 * 单例bean默认非懒加载
 *
 * {@link BeanDefinition#Object()}
 * {@link AnnotationBeanDe}
 *
 */
@Slf4j
@Scope(LScope.SCOPE_NAME) //单例bean默认非懒加载。其它Scope默认懒加载
@Lazy(value = true) //不是懒加载失效，而是有东西调用了FactoryBean，
@Component
public class LBean implements FactoryBean<LBean.MyBean>, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware,
        EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, MessageSourceAware, ApplicationStartupAware,
        ApplicationEventPublisherAware, ApplicationContextAware, ServletContextAware, ServletConfigAware, ImportAware,
        InitializingBean, DisposableBean, Statistical, Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    private static volatile boolean isGetBeanTypeLogged = false;

    private int order = 0;

    @Value("${spring.profiles.active}")
    private String active;

    /**
     * 依赖注入靠 {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean(String, RootBeanDefinition, BeanWrapper)}
     * {@link org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor}
     */
    @Autowired
    private DependentBean dependentBean;

    /**
     * 通过构造器注入,那么dependentBean的构造器被优先调用.
     * <p>
     * 为了核验类型，构造方法也会被多次调用
     * {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getNonSingletonFactoryBeanForTypeCheck(String, RootBeanDefinition)}
     */
    @Autowired
    private LBean(DependentBean dependentBean){
        if(!isGetBeanTypeLogged){
            Statistics.logInfo(log, this,"实例化", dependentBean);
            isGetBeanTypeLogged = true;
        }
    }

    @Override
    public synchronized int gas(){
        return order++;
    }

    /**
     *  这个方法会在 {@link org.springframework.beans.factory.support.DefaultListableBeanFactory#doGetBeanNamesForType(ResolvableType, boolean, boolean)}
     *  {@link org.springframework.beans.factory.support.AbstractBeanFactory#isTypeMatch(String, ResolvableType, boolean)}
     *  {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getTypeForFactoryBean(FactoryBean)}
     *  被多次调用
     */
    @Override
    public Class<?> getObjectType() {
        if (!isGetBeanTypeLogged){
            Statistics.logInfo(log, this, "getObjectType", "");
            isGetBeanTypeLogged = true;
        }
        return MyBean.class;
    }

    @Autowired
    public void setDependentBean(DependentBean dependentBean){
        Statistics.logInfo(log, this, "setDependentBean", dependentBean);
    }

    /**
     * 以下{@link Aware} 在<br>
     * {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods(String, Object)}
     * 中处理:<br>
     * {@link BeanNameAware}
     * {@link BeanClassLoaderAware}
     * {@link BeanFactoryAware}
     */
    @Override
    public void setBeanName(@NonNull String name) {
        Statistics.logInfo(log, this, "setBeanName", name);
    }

    @Override
    public void setBeanClassLoader(@NonNull ClassLoader classLoader) {
        Statistics.logInfo(log, this, "setBeanClassLoader", classLoader);
    }

    @Override
    public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
        Statistics.logInfo(log, this, "setBeanFactory", beanFactory);
    }

    /**
     * 下列{@link Aware} 在 {@link org.springframework.context.support.ApplicationContextAwareProcessor}
     * 中先后处理:<br>
     * {@link EnvironmentAware}
     * {@link EmbeddedValueResolverAware}
     * {@link ResourceLoaderAware}
     * {@link ApplicationEventPublisherAware}
     * {@link MessageSourceAware}
     * {@link ApplicationStartupAware}
     * {@link ApplicationContextAware}
     */

    @Override
    public void setEnvironment(@NonNull Environment environment) {
        Statistics.logInfo(log, this, "setEnvironment", environment);
    }

    @Override
    public void setEmbeddedValueResolver(@NonNull StringValueResolver resolver) {
        Statistics.logInfo(log, this, "setEmbeddedValueResolver", resolver);
    }

    @Override
    public void setResourceLoader(@NonNull ResourceLoader resourceLoader) {
        Statistics.logInfo(log, this, "setResourceLoader", resourceLoader);
    }

    @Override
    public void setApplicationEventPublisher(@NonNull ApplicationEventPublisher applicationEventPublisher) {
        Statistics.logInfo(log, this, "setApplicationEventPublisher", applicationEventPublisher);
    }

    @Override
    public void setMessageSource(@NonNull MessageSource messageSource) {
        Statistics.logInfo(log, this, "setMessageSource", messageSource);
    }

    @Override
    public void setApplicationStartup(@NonNull ApplicationStartup applicationStartup) {
        Statistics.logInfo(log, this, "setApplicationStartup", applicationStartup);
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        Statistics.logInfo(log, this, "setApplicationContext", applicationContext);
    }

    /**
     * 下列在 <br>
     * {@link org.springframework.web.context.support.ServletContextAwareProcessor#postProcessBeforeInitialization(Object, String)}
     * 中先后处理:
     * {@link ServletContextAware}
     * {@link ServletConfigAware}
     */

    @Override
    public void setServletContext(@NonNull ServletContext servletContext) {
        Statistics.logInfo(log, this, "setServletContext", servletContext);
    }

    @Override
    public void setServletConfig(@NonNull ServletConfig servletConfig) {
        Statistics.logInfo(log, this, "setServletConfig", servletConfig);
    }

    /**
     * 以下{@link Aware} 在
     * {@link org.springframework.context.annotation.ConfigurationClassPostProcessor.ImportAwareBeanPostProcessor#postProcessBeforeInitialization(Object, String)}
     * 中处理:
     * {@link ImportAware}
     */
    @Override
    public void setImportMetadata(@NonNull AnnotationMetadata importMetadata) {
        Statistics.logInfo(log, this, "setImportMetadata", importMetadata);
    }

    /**
     * 该注解在
     * {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor}
     * 中处理，该类是
     * {@link org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor}
     * 的实现类，而后者又是
     * {@link DestructionAwareBeanPostProcessor}的实现类
     *
     * <p>
     *     这么取名的用意是， 在构造后
     * </p>
     */
    @PostConstruct
    public void postConstruct() {
        Statistics.logInfo(log, this, "postConstruct", "");
    }

    /**
     * 该方法在
     * {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods(String, Object, RootBeanDefinition)}
     * 中调用
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        Statistics.logInfo(log, this, "afterPropertiesSet", "");
    }

    @Override
    public MyBean getObject() throws Exception {
        Statistics.logInfo(log, this, "getObject", "");
        return new MyBean();
    }

    /**
     * 该方法在<br>
     * {@link org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction(Object, String)}
     * 中处理
     * <p>
     * 只有单例才会被回调
     */
    @PreDestroy
    public void preDestroy() {
        Statistics.logInfo(log, this, "preDestroy", "");
    }

    /**
     * 该方法因{@link Scope} 不同而被不同类调用。
     * 具体逻辑在:
     * {@link org.springframework.beans.factory.support.AbstractBeanFactory#registerDisposableBeanIfNecessary}
     *
     * 原型bean不会在容器关闭时调用此方法.
     * 自定义Scope需要在{@link org.springframework.beans.factory.config.Scope#registerDestructionCallback(String, Runnable)}
     * 实现
     * <p>
     * 只有单例才会回调此方法
     */
    @Override
    public void destroy() throws Exception {
        Statistics.logInfo(log, this, "destroy", "");
    }

    @Slf4j
    @Component
    @Scope(BeanDefinition.SCOPE_SINGLETON)
    @Lazy(value = true)
    public static class DependentBean{

        private DependentBean(){
            Statistics.logInfo(log);
        }
    }

    public static class MyBean{

        private static final Logger log = LoggerFactory.getLogger(MyBean.class);

        private MyBean(){
            Statistics.logInfo(log);
        }

    }
}
